﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WorkStation.Frameworks.Core;
using WorkStation.Frameworks.Core.Extensions;
using WorkStation.Frameworks.EasyBus.DbModel;
using WorkStation.Frameworks.EasyBus.MessageQueue;
using WorkStation.Frameworks.EasyBus.Pipeline;
using WorkStation.Frameworks.EasyBus.Pipeline.Filters;
using WorkStation.Frameworks.EasyBus.Pipeline.Handles;

namespace WorkStation.Frameworks.EasyBus.Handles
{
    public class MessageRouteHandle : Handle
    {
        public static readonly string NAME = "EasyBus.MessageRouteHandle";

        protected Dictionary<string, SubscribeInfo[]> _subscribeInfoCache = new Dictionary<string, SubscribeInfo[]>();
        protected Dictionary<string, Dictionary<string, SubscribeInfo[]>> _balanceCache = new Dictionary<string, Dictionary<string, SubscribeInfo[]>>();

        public MessageRouteHandle() 
        {
            this.Name = NAME;
            this.Order = -100;
        }

        public MessageRouteHandle Init(IBus bus) 
        {
            IBusDataManager manager = bus as IBusDataManager;

            string[] routeKey = bus.ConfigProvider.SelectMany(v => v.GetPublishInfo().Select(p => p.RouteKey)).ToArray();

            routeKey.ForEach(key => 
            {
                SubscribeInfo[] subs = (bus.DataBag["SubscribeInfo"] as List<SubscribeInfo>).ToArray();

                this._subscribeInfoCache[key] = subs;

                // 按订阅端BusName分组
                var dict = subs.GroupBy(v => new { v.BusName, v.Name }).Where(v => v.Count() > 1).ToDictionary(v => "{0}-{1}".Fmt(v.Key.BusName, v.Key.Name), v => v.ToArray());

                (dict.Count != 0).WhenTrue(() => this._balanceCache[key] = dict);
            });

            return this;
        }

        protected override HandleResult InnerExecute(ActionContext context)
        {
            try
            {
                Message msg = context.Input as Message;

                IBus bus = (context as BusActionContext).Bus;

                IBusDataManager manager = bus as IBusDataManager;
                
                Dictionary<string, SubscribeInfo[]> cache = null;

                // 处理软负载
                if (this._balanceCache.TryGetValue(context.RouteKey, out cache))
                {
                    cache.ForEach(v =>
                    {
                        string node = v.Value.FirstByRandom().NodeName;

                        string[] ignoreHandle = v.Value.Where(s => s.NodeName.Equals(node)).Select(s => DefaultPublishHandle.GetHandleName(s)).ToArray();

                        (context.Args[ActionContext.IGNORE_HANDLE_KEY] as List<string>).AddRange(ignoreHandle);
                    });
                }

                if (msg.IsRecordingHistory)
                {
                    // 这块去掉了等待上传消息日志任务完成的代码，因为如果在异步的 WebApi 或者 Asp.Net 程序时（在使用 Task.WaitAll 的时候），有可能造成死锁。如果消息日志不存在则可以认为是没有发布日志，不用考虑去除发布成功的
                    MessageLog log = manager.HistoryCenterRepository.GetMessageLogById(msg.MessageId);
                    
                    SubscribeInfo[] subs = null;

                    if (log != null && this._subscribeInfoCache.TryGetValue(context.RouteKey, out subs))
                    {
                        // 去除发送成功的订阅
                        Tuple<string, string>[] ignoreSubscribe = log.PublishLogs.Where(v => v.IsSuccess).Select(v => new Tuple<string, string>(v.SubscribeBusName, v.SubscribeName)).ToArray();

                        (context.Args[ActionContext.IGNORE_HANDLE_KEY] as List<string>).AddRange(subs.Where(v => ignoreSubscribe.Any(s => s.Item1.Equals(v.BusName) && s.Item2.Equals(v.Name))).Select(v => DefaultPublishHandle.GetHandleName(v)));
                    }
                }
            }
            catch (Exception ce) 
            {
                context.IsContinue = false;

                // 注册中心或者历史记录中心异常，总线不能继续运行
                return new MessageRouteHandleErrorResult(context.Input, this, ce.ToString(), ce);
            }

            return new SuccessHandleResult(context.Input, this, "{0} process success.".Fmt(typeof(MessageRouteHandle).FullName));
        }
    }

    public class MessageRouteHandleErrorResult : FailHandleResult
    {
        public MessageRouteHandleErrorResult(object input, IHandle handle, string error = null, Exception e = null)
            : base(input, handle, error, e)
        { }
    }
}
